package com.bthdung.studios.lesson12.glUtil;

import android.graphics.Bitmap;
import android.graphics.Color;
import android.util.FloatMath;

/*
 * Creates the terrain data only using arrays of floats
 * Good: Fast, memory efficient
 * Bad : Difficult to manipulate vertices, since each vertex as 8 floats in a large array
*/
public class OptimizedLab
{
	// Size in elements (floats)
	public static final int posSize = 3;
	public static final int normSize = 3;
	public static final int texSize = 2;

	// Number of floats in a vertex
	public static final int vertSize = posSize + normSize + texSize;

	// Creates the terrain grid, normals are calculated later when the grid is
	// ready
	public static float[] genGrid(int width, int height, float scale, Bitmap map)
	{
		int vertsCount = width * height;

		float[] verts = new float[vertSize * vertsCount];

		int index = 0;

		for (int z = 0; z < height; z++)
		{
			for (int x = 0; x < width; x++)
			{
				// Position
				verts[index++] = scale * (x - (width - 1) / 2f);
				verts[index++] = scale * (float) Color.red(map.getPixel(x, z)) / 7f;
				verts[index++] = scale * (z - (height - 1) / 2f);

				// Normal
				verts[index++] = 0;
				verts[index++] = 0;
				verts[index++] = 0;

				// TexCoord
				verts[index++] = (float) x / (float) (width - 1);
				verts[index++] = (float) z / (float) (height - 1);
			}
		}

		return verts;
	}

	// Gets the index of the first float of a normal for a specific vertex
	static int getNormalStart(int vertIndex)
	{
		return vertIndex * vertSize + posSize;
	}

	// Gets the index of the first float of a specific vertex
	static int getPositionStart(int vertIndex)
	{
		return vertIndex * vertSize;
	}

	// Sets the normal of a vertex to the provided value
	public static void setNormal(int vertIndex, float[] verts, float x, float y, float z)
	{
		int i = getNormalStart(vertIndex);

		verts[i] = x;
		verts[i + 1] = y;
		verts[i + 2] = z;
	}

	// Adds the provided value to the normal
	public static void addNormal(int vertIndex, float[] verts, float x, float y, float z)
	{
		int i = getNormalStart(vertIndex);

		verts[i] += x;
		verts[i + 1] += y;
		verts[i + 2] += z;
	}

	public static void normalizeNormal(int vertIndex, float[] verts)
	{
		int i = getNormalStart(vertIndex);

		float x = verts[i];
		float y = verts[i + 1];
		float z = verts[i + 2];

		float num2 = ((x * x) + (y * y)) + (z * z);
		float num = 1f / FloatMath.sqrt(num2);
		x *= num;
		y *= num;
		z *= num;

		verts[i] = x;
		verts[i + 1] = y;
		verts[i + 2] = z;
	}

	public static void calcNormals(short[] indices, float[] verts)
	{
		for (int i = 0; i < indices.length; i += 3)
		{
			int i1 = getPositionStart(indices[i]);
			int i2 = getPositionStart(indices[i + 1]);
			int i3 = getPositionStart(indices[i + 2]);

			// p1
			float x1 = verts[i1];
			float y1 = verts[i1 + 1];
			float z1 = verts[i1 + 2];

			// p2
			float x2 = verts[i2];
			float y2 = verts[i2 + 1];
			float z2 = verts[i2 + 2];

			// p3
			float x3 = verts[i3];
			float y3 = verts[i3 + 1];
			float z3 = verts[i3 + 2];

			// u = p3 - p1
			float ux = x3 - x1;
			float uy = y3 - y1;
			float uz = z3 - z1;

			// v = p2 - p1
			float vx = x2 - x1;
			float vy = y2 - y1;
			float vz = z2 - z1;

			// n = cross(v, u)
			float nx = (vy * uz) - (vz * uy);
			float ny = (vz * ux) - (vx * uz);
			float nz = (vx * uy) - (vy * ux);

			// normalize(n)
			float num2 = ((nx * nx) + (ny * ny)) + (nz * nz);
			float num = 1f / FloatMath.sqrt(num2);
			nx *= num;
			ny *= num;
			nz *= num;

			addNormal(indices[i], verts, nx, ny, nz);
			addNormal(indices[i + 1], verts, nx, ny, nz);
			addNormal(indices[i + 2], verts, nx, ny, nz);
		}

		for (int i = 0; i < (verts.length / vertSize); i++)
			normalizeNormal(i, verts);
	}

}
